import os
import shutil

import pyttsx3 as pyttsx3
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QCursor
from PyQt5.QtWidgets import QPushButton, QLineEdit, QPlainTextEdit, QSpinBox, QDoubleSpinBox, QComboBox, QListWidget, \
    QMessageBox, QLabel, QMenu, QAction, QListWidgetItem, QScrollBar
from PyQt5.uic import loadUi

from base_api.base import PygameMixerTaskClass
from base_api.base.MessageClass import Message
from base_api.base.PygameMixerTaskClass import PygameMixerTask
from base_api.base.Pyttsx3TaskClass import Pyttsx3Task
from base_api.base.VideoInfoClass import VideoInfo
from pyqt_api.PyqtThreadPoolClass import PyqtThreadPool

"""
语音合成子窗口
"""


class SynthesisWindow:
    def __init__(self, home_window):
        self.window = loadUi('ui/synthesis.ui')
        self.home_window = home_window

        # 待播放音频文件的路径
        self.this_video_path = ''
        # 待播放音频文件的信息
        self.this_video_info = None
        """
        两个时间节点，float类型，单位秒
        1表示当前音频的开始节点，2表示当前音频的实时播放节点。
        2有三种场景
        (1)如果在播放过程中，2由1和播放器实时反馈的播放时长决定，进而设置进度条的显示。
        (2)如果在播放停止时，拖拽进度条，会根据判断是人为操作影响，对2基于进度条进行赋值。
        (3)点击树组件或者上一首、下一首，切换音频时，1和2都置为0
        在决定播放音频时，会根据2设置1
        """
        self.this_video_point_1 = 0.0
        self.this_video_point_2 = 0.0

        # 进度条组件
        self.window.horizontalScrollBar: QScrollBar
        self.window.horizontalScrollBar.setMinimum(0)
        self.window.horizontalScrollBar.setMaximum(100)
        self.window.horizontalScrollBar.setValue(0.0)
        self.window.horizontalScrollBar.valueChanged.connect(self.horizontalScrollBar_valueChanged)

        # 播放音频任务对象
        self.pygameMixerTask = PygameMixerTask()
        # 线程对象
        self.pyqtThreadPool = None
        # 工作状态，0表示未开始/结束，1表示运行中
        self.work_status = 0

        # 合成任务对象
        self.pyttsx3Task = Pyttsx3Task()

        # 文本监控
        self.window.plainTextEdit: QPlainTextEdit
        self.window.plainTextEdit.textChanged.connect(self.plainTextEdit_textChanged)
        # 速率
        self.window.spinBox: QSpinBox
        self.window.spinBox.setMaximum(500)
        self.window.spinBox.setValue(200)
        # 音量
        self.window.doubleSpinBox: QDoubleSpinBox
        self.window.doubleSpinBox.setMaximum(1.0)
        self.window.doubleSpinBox.setMinimum(0.0)
        self.window.doubleSpinBox.setValue(1.0)
        # 引擎
        self.window.comboBox: QComboBox
        self.window.comboBox.clear()
        self.pyttsx3Task.init_voice_list()
        for i in range(len(self.pyttsx3Task.voice_list)):
            self.window.comboBox.addItem(self.pyttsx3Task.voice_list[i].name)

        self.window.comboBox_2: QComboBox
        self.window.comboBox_2.addItems(['不分割', '换行符分割', '空行分割', '句号。分割', '空格分割'])
        # 合成按钮
        self.window.pushButton: QPushButton
        self.window.pushButton.clicked.connect(self.work)
        self.window.pushButton_2: QPushButton
        self.window.pushButton_2.clicked.connect(self.control)

        """缓冲区"""
        self.window.listWidget: QListWidget
        # 右键菜单
        self.window.listWidget.setContextMenuPolicy(Qt.CustomContextMenu)
        self.window.listWidget.customContextMenuRequested.connect(self.listWidget_rightmenu)
        # 点击项
        self.window.listWidget.itemClicked.connect(self.listWidget_itemClicked)
        self.this_video_path = ''
        self.this_video_info = None
        self.start = 0

    def horizontalScrollBar_valueChanged(self, value):
        # 未执行状态, 则进度条变化是人为拖拽或者初始归零设置
        if self.work_status == 0:
            if value == 0:
                self.this_video_point_2 = 0.0
            else:
                # 基于value计算时间，单位秒
                self.window.horizontalScrollBar: QScrollBar
                self.this_video_point_2 = value/self.window.horizontalScrollBar.maximum()*self.this_video_info.length_sec
                # 保留两位小数
                self.this_video_point_2 = int(self.this_video_point_2*100)/100
            # 同步拖拽或归零操作到标签，显示单位为秒
            self.window.label_7: QLabel
            self.window.label_7.setText(str(self.this_video_point_2))

    def set_this_video(self, path: str, flag=True):
        self.this_video_path = path
        # 解析信息
        self.this_video_info = VideoInfo(self.this_video_path)
        # 归位0
        self.window.horizontalScrollBar: QScrollBar
        self.window.horizontalScrollBar.setValue(0)

    def plainTextEdit_textChanged(self):
        self.window.plainTextEdit: QPlainTextEdit
        text = self.window.plainTextEdit.toPlainText()
        self.window.label: QLabel
        self.window.label.setText('字数:%s,行数:%s' % (str(len(text)), str(text.count('\n')+1)))

    def listWidget_rightmenu(self, pos):
        menu = QMenu(self.window.listWidget)
        menu.addAction('导出选中项')
        menu.addAction('导出全部项')
        menu.triggered.connect(self.listWidget_rightmenu_triggered)
        # 将菜单显示在鼠标位置
        menu.exec_(QCursor.pos())

    def listWidget_rightmenu_triggered(self, act: QAction):
        pass

    def listWidget_itemClicked(self, item: QListWidgetItem):
        if item is not None:
            self.this_video_path = self.home_window.this_workspace_dir + '/temp1/' + item.text() + '.wav'
            self.this_video_info = VideoInfo(self.this_video_path)
            self.this_video_point_1 = 0.0
            self.this_video_point_2 = 0.0

    def work(self):
        # 清除缓冲区
        self.window.listWidget: QListWidget
        self.window.listWidget.clear()
        shutil.rmtree(self.home_window.this_workspace_dir + '/temp1')
        os.mkdir(self.home_window.this_workspace_dir + '/temp1')
        # 清除当前音频选项
        self.this_video_path = ''

        # 读取文本
        self.window.plainTextEdit: QPlainTextEdit
        text = self.window.plainTextEdit.toPlainText()

        # 读取分隔符
        self.window.comboBox_2: QComboBox
        sep = ['', '\n', '\n\n', '。', ' '][self.window.comboBox_2.currentIndex()]
        # 处理文本
        if text != '':
            split = [text]
            if sep != '':
                split = text.split(sep)
            # 遍历文本以合成，及输出到缓存
            engine = pyttsx3.init()
            self.window.spinBox: QSpinBox
            engine.setProperty('rate', self.window.spinBox.value())
            self.window.doubleSpinBox: QDoubleSpinBox
            engine.setProperty('volumn', self.window.doubleSpinBox.value())
            self.window.comboBox: QComboBox
            engine.setProperty('voice', self.pyttsx3Task.voice_list[self.window.comboBox.currentIndex()].id)

            for i in range(len(split)):
                engine.save_to_file(split[i], filename=self.home_window.this_workspace_dir + '/temp1/' +
                                    str(i + 1) + '.wav')
                engine.runAndWait()
                self.window.listWidget.addItem(str(i + 1))
        QMessageBox.information(self.window, '提示', '完成')

    def control(self):
        # 未开始
        if self.work_status == 0:
            # 如果没有选中项，警告
            if self.this_video_path == '' or not os.path.exists(self.this_video_path):
                QMessageBox.warning(self.window, '警告', '未选中任何音频或者选中音频不存在')
                return
            # 当前有其它子窗口的播放器对象在工作
            if not PygameMixerTaskClass.get_chance():
                QMessageBox.warning(self.window, '警告', '当前有其它音频正在播放，请手动关闭或等待')
                return
                # 待变更为直接关闭其它子窗口的工作，然后执行当前子窗口的播放任务
            """拿到音频播放标志"""
            # 线程对象创建
            self.pyqtThreadPool = PyqtThreadPool()
            # 线程对象的消息位初始化
            self.pyqtThreadPool.get_signal()
            if self.pyqtThreadPool.signal_this is None:
                QMessageBox.warning(self.window, '警告', '线程池繁忙，请稍后尝试或者手动关闭某线程工作')
                # 用不上就释放
                PygameMixerTaskClass.set_isBusy_flag(False)
            else:
                # 进行相关检查和赋值
                flag = self.init_pygameMixerTask()
                if flag:
                    # 切换到工作状态
                    self.work_status = 1
                    # 变更按钮文本
                    self.window.pushButton_2: QPushButton
                    self.window.pushButton_2.setText('播放中(点击暂停)')
                    # 任务对象的执行方法赋值给线程对象的属性
                    self.pyqtThreadPool.run_task = self.pygameMixerTask.exploit
                    # 绑定消息位
                    self.pyqtThreadPool.signal_this.connect(self.output)
                    # 绑定消息位
                    self.pygameMixerTask.output_access = self.pyqtThreadPool.output
                    # 开启线程
                    self.pyqtThreadPool.start()
                else:
                    # 用不上就释放
                    PygameMixerTaskClass.set_isBusy_flag(False)
        # 正在运行中
        else:
            self.work_status = 0
            self.pygameMixerTask.stop()
            self.window.pushButton_2.setText('播放')

    def output(self, msg: Message):
        print(msg.status, msg.data)
        # 任务执行过程中出现异常
        if msg.status == -3:
            self.work_status = 0
            QMessageBox.warning(self.window, '警告', f'任务执行过程中出现异常，已结束。{msg.data}')
            PygameMixerTaskClass.set_isBusy_flag(False)
        # 消息类结束执行。在测试功能中，不会再开子线程，意味着其表示本次任务的完成
        elif msg.status == -1:
            self.work_status = 0
            PygameMixerTaskClass.set_isBusy_flag(False)
            self.window.pushButton_2: QPushButton
            self.window.pushButton_2.setText('播放')

        # 音频播放进度反馈
        elif msg.status == 1:
            self.window.horizontalScrollBar: QScrollBar
            self.window.label_7: QLabel
            # 为-1表示音频播放完成，直接赋值为音频的最大长度
            if msg.data == -1:
                self.this_video_point_2 = int(self.this_video_info.length_sec*100)/100
                temp = self.window.horizontalScrollBar.maximum()
            # 中间过程，传递的是播放时长，单位毫秒
            else:
                # 计算音频当前播放节点
                self.this_video_point_2 = self.this_video_point_1 + msg.data/1000
                temp = int(self.this_video_point_2/self.this_video_info.length_sec *
                           self.window.horizontalScrollBar.maximum())
            # 标签标记进度，单位秒
            self.window.label_7.setText(str(int(self.this_video_point_2*100)/100))
            # 进度条设置
            self.window.horizontalScrollBar.setValue(temp)

    def init_pygameMixerTask(self):
        # 如果当前音频文件为空，或者被删除了不存在，返回False
        if self.this_video_path == '' or not os.path.exists(self.this_video_path):
            return False
        # 初始化待播放音频的文件路径
        self.pygameMixerTask.this_video_path = self.this_video_path
        """ 初始化待播放音频的开始时间 """
        self.window.horizontalScrollBar: QScrollBar
        # 如果此时是末尾，会自动切换为0
        if self.window.horizontalScrollBar.value() == self.window.horizontalScrollBar.maximum():
            self.window.horizontalScrollBar.setValue(0)
            self.this_video_point_1 = 0.0
            self.this_video_point_2 = 0.0
            self.window.label_7.setText('0.0')

        self.this_video_point_1 = self.this_video_point_2
        self.pygameMixerTask.this_start_point = self.this_video_point_1
        return True
